איך לתמוך במיליוני מבקרים בחודש עם שרת בפחות מ100$
האצת עמודים דינאמיים ותמיכה ביותר מבקרים בעזרת Varnish Cache
(או - איך לתמוך במיליוני מבקרים בחודש עם שרת ב100$)
במאמר זה אציג את Varnish Cache כפתרון להאצת אתרים בעלי עמודים דינאמיים ואראה כיצד ניתן לתמוך בהרבה יותר משתמשים תוך כדי האצת ביצועי השרת באופן משמעותי.
מהאתר של Varnish:
"Varnish Cache is an open source, state of the art web application accelerator. You install it on your web server and it makes your website fly."
אתרים רבים מריצים PHP (או שפה אחרת לצורך העניין) ובכל העלאת עמוד מריצים מאותך ואלפי שורות קוד, מבצעים קריאות מבסיס נתונים ועם זאת מציגים את אותו התוכן לרוב המבקרים. רוב העמודים לא משתנים לעיתים קרובות כל כך, כך שמדובר בעבודה מיותרת של השרת.
אלכס הציג פתרון לא רע למקרים רבים, שבו נבצע caching לחלק מהעמוד או לכולו ובכך נחסוך את הרצת הקוד. לאפשרות הזו יש מספר יתרונות - היא לא דורשת התקנה וניהול של תוכנה נוספת וניתן ליישם אותה גם ב Shared Hosting כאשר אין לנו אפשרות להתקין תוכנה על השרת. החיסרונות הם:
1) אנחנו נדרשים לכתוב פתרון משלנו בקוד, דבר הדורש ניהול ודיבוג.
2) זה עדיין רץ על Apache
למה אני רושם את שרת Apache כחיסרון? כי למרות היותו שרת ה WEB מהנפוצים בעולם, Apache הוא שרת לא יעיל במיוחד (אל דאגה, אני לא הולך להציג את IIS כאלטרנטיבה למרות שנותיי הרבות כמומחה תשתיות מיקרוסופט..) - כל Process של Apache יכול לצרוך בסביבות 5MB-50MB של זיכרון, אפילו כשהוא מגיש קבצים סטטיים כמו CSS, JS, PNG. לכן, בשרתים המריצים Apache, צוואר הבקבוק הוא בד"כ כמות הזיכרון בשרת.
מעבר לכך ש Apache צורך הרבה זיכרון, הוא גם די איטי ביחס לפתרונות מהירים יותר (כמו Nginx ו Varnish למשל).
באתרים עם כמות תנועה קטנה-בינונית ועם קוד יעיל, יכול להיות שלא נרגיש את החסרונות הללו, אולם כשיש פיקים של תנועה השרת יכול להתחיל לזחול.
לדוגמא - אם יש לנו 600MB זיכרון פנוי בשרת וכל process של apache צורך 30MB זיכרון, נוכל לתמוך בעשרים בקשות בו זמנית מבלי שהשרת יתחיל להיחנק ולהשתמש בזיכרון הווירטואלי. אם לייצר את העמוד שלנו לוקח לדוגמא, חצי שניה, נוכל לתמוך בארבעים משתמשים בו זמנית. לא הרבה במיוחד, וזה יהיה גרוע יותר אם השרת עמוד ומתחיל לקחת לו שניה וחצי לייצר עמוד.
אז אחרי ההקדמה ה"קצרה", אציג את הפתרון שמספק Varnish:
Varnish מטפל בכל הפניות שמגיעות לשרת ומשמש כ proxy מול ה apache שלנו. כשמגיעה בקשה ל varnish הוא בודק אם יש לו כבר את העמוד ב cache, אם כן, יגיש את העמוד מהcache (בהתאם להגדרות שלנו לאורך חיי עמוד בcache, למי להגיש מcache ולמי לא וכו') שהוא בעצם קובץ סטטי ולכן זמן התגובה הוא מהיר מאוד. אם העמוד לא נמצא בקאש, varnish יפנה ל apache ויבקש את העמוד, יגיש אותו ללקוח וכמו כן יכניס אותו לקאש ובכך מוריד עומס רב מה apache שאליו יגיעו בסופו של דבר רק חלק קטן מאוד מהבקשות.
מעבר לחסכון בהרצת סקריפטים PHP, פניות ל MySQL וכו', יש לvarnish יתרון נוסף.
ציינתי כבר ש apache הוא לא מהיר במיוחד וגם כאן יש לvarnish יתרון - הוא פשוט הרבה יותר מהיר גם בהגשת התכנים מהקאש וגם בהגשת הקבצים הסטטיים שלנו. אבל אתם לא חייבים להאמין לי, יש לי דוגמא:
הנה דוגמא אמיתית מהשרת של drippler של ביצוע 200 פניות לשרת לקבלת קובץ JS, כאשר מבוצעות 100 פניות במקביל בכל פעם. פעם ראשונה מול apache ופעם שניה מול varnish. מבוצע באמצעות ab (apache benchmark):
Server Software: Apache/2.x.xx
Server Hostname: drippler.com
Server Port: 8282
Document Path: /sites/default/files/js/js_0NdHKxgqhxXfmHCwZepV3aSuD_H9T7ZP4HJzc-QVyio.js
Document Length: 17968 bytes
Concurrency Level: 100
Time taken for tests: 0.247 seconds
Complete requests: 200
Failed requests: 0
Write errors: 0
Total transferred: 3804246 bytes
HTML transferred: 3719376 bytes
Requests per second: 809.30 [#/sec] (mean)
Time per request: 123.564 [ms] (mean)
Time per request: 1.236 [ms] (mean, across all concurrent requests)
Transfer rate: 15033.04 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 17 10.6 17 28
Processing: 23 81 29.4 93 128
Waiting: 2 74 31.7 88 127
Total: 31 98 26.3 105 145
Percentage of the requests served within a certain time (ms)
50% 105
66% 111
75% 115
80% 116
90% 127
95% 133
98% 141
99% 143
100% 145 (longest request)
Server Hostname: drippler.com
Server Port: 8282
Document Path: /sites/default/files/js/js_0NdHKxgqhxXfmHCwZepV3aSuD_H9T7ZP4HJzc-QVyio.js
Document Length: 17968 bytes
Concurrency Level: 100
Time taken for tests: 0.247 seconds
Complete requests: 200
Failed requests: 0
Write errors: 0
Total transferred: 3804246 bytes
HTML transferred: 3719376 bytes
Requests per second: 809.30 [#/sec] (mean)
Time per request: 123.564 [ms] (mean)
Time per request: 1.236 [ms] (mean, across all concurrent requests)
Transfer rate: 15033.04 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 17 10.6 17 28
Processing: 23 81 29.4 93 128
Waiting: 2 74 31.7 88 127
Total: 31 98 26.3 105 145
Percentage of the requests served within a certain time (ms)
50% 105
66% 111
75% 115
80% 116
90% 127
95% 133
98% 141
99% 143
100% 145 (longest request)
Server Software: Apache/2.x.xx
Server Hostname: drippler.com
Server Port: 80
Document Path: /sites/default/files/js/js_0NdHKxgqhxXfmHCwZepV3aSuD_H9T7ZP4HJzc-QVyio.js
Document Length: 17968 bytes
Concurrency Level: 100
Time taken for tests: 0.032 seconds
Complete requests: 200
Failed requests: 0
Write errors: 0
Total transferred: 3686600 bytes
HTML transferred: 3593600 bytes
Requests per second: 6321.11 [#/sec] (mean)
Time per request: 15.820 [ms] (mean)
Time per request: 0.158 [ms] (mean, across all concurrent requests)
Transfer rate: 113786.20 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 3 6 2.1 6 10
Processing: 3 8 2.3 8 12
Waiting: 1 6 2.6 6 10
Total: 13 15 0.8 15 16
Percentage of the requests served within a certain time (ms)
50% 15
66% 15
75% 15
80% 16
90% 16
95% 16
98% 16
99% 16
100% 16 (longest request)
Server Hostname: drippler.com
Server Port: 80
Document Path: /sites/default/files/js/js_0NdHKxgqhxXfmHCwZepV3aSuD_H9T7ZP4HJzc-QVyio.js
Document Length: 17968 bytes
Concurrency Level: 100
Time taken for tests: 0.032 seconds
Complete requests: 200
Failed requests: 0
Write errors: 0
Total transferred: 3686600 bytes
HTML transferred: 3593600 bytes
Requests per second: 6321.11 [#/sec] (mean)
Time per request: 15.820 [ms] (mean)
Time per request: 0.158 [ms] (mean, across all concurrent requests)
Transfer rate: 113786.20 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 3 6 2.1 6 10
Processing: 3 8 2.3 8 12
Waiting: 1 6 2.6 6 10
Total: 13 15 0.8 15 16
Percentage of the requests served within a certain time (ms)
50% 15
66% 15
75% 15
80% 16
90% 16
95% 16
98% 16
99% 16
100% 16 (longest request)
ניתן לראות של apache לקח בממוצע 100 מילי שניות להגיש את הקובץ, בעוד שלvarnish לקחת כ15 מילישניות. הבדל משמעותי מאוד במהירות וגם בעומס על השרת היות וapache היה צריך לפתוח process כבד עבור כל בקשה שכזאת.
אם נבצע את אותה ההשוואה לעמודים דינאמיים, נקבל הבדלים הרבה יותר קיצוניים (מתוך רחמים על השרת שלנו ועל המשתמשים והתעצלות להעלות שרת אחר, לא אבצע את ההשוואה הזאת בפועל עכשיו) - ההבדלים יהיו שניות בapache מול מילישניות בvarnish.
היות וvarnish מאפשר לנו להגיש עמודים דינאמיים מהcache והודות לכך יכול לטפל באלפי בקשות בשניה, אם תעשו את החשבון תראו שזה יאפשר לנו לשרת מיליוני ועשרות מיליוני מבקרים בחודש בלי שום בעיה ובלי מערך של שרתים מפלצתיים.
עכשיו בטח תשאלו - מה עם המשתמשים הרשומים שלנו שצריכים לקבל תוכן דינאמי? ואולי יש לכם סוג נוסף של מבקרים שצריכים לקבל תוכן דינאמי בהתאם לעוגיות, user-agent וכו'?
הפתרון הוא לקנפג את varnish להגיש תוכן מהקאש רק בתנאים מסויימים (לדומא אם אין למשתמש עוגיות מסויימות, אוטנטיקציה וכו'). כך נפחית את העומס מהשרת ועם זאת עדיין נגיש תוכן דינאמי למי שצריך.
היות והמאמר יצא ארוך יותר מהמתוכנן, לא ארחיב על איך להתקין ולהגדיר שרת varnish. אם אראה שיש התעניינות בנושא אשתדל לכתוב פוסט נוסף עם טיפים איך לקנפג את שרת varnish.
תגובות לכתבה:
מדריך מפורט ומוסבר כמו שצריך ))
הלכתי לבדוק את זה עכשיו,
תודה.
מדריך מעולה :)
+1 , בודק את זה גם.
אחלה
אשמח לשמוע איך הלך לכם עם זה
ל- Zend Server יש פתרון כזה כבר שנים שנקרא Page Cache ששומר בקאש דפים שלמים, ויכול לפעול באמצעות מגוון אופציות כמו דפים מסוימים, עוגיות, קוד מסוים או ביטוי רגולרי. משפר ביצועים בטירוף אך דורש התקנת Zend Server ולא ניתן להתקין על Shared host.
הפתרון של זנד לא דורש התערבות קוד בכלל, יתרון גדול מאוד.
בינינו מי שיש לו אתר עמוס מאוד לא שם את השרת על Shared host אלא קונה מכונה בענן או ממש פיזית ומתקין עליה מה שהוא רוצה.
יכול להיות שאתה צודק, אבל בו לא נשכח שרשיון לZend Server עולה כ1200$ לשנה לגירסא הבסיסית, לא לכולם יש כסף כזה לזרוק. במיוחד אם הבנאדם צריך רק דבר אחד-שתיים ולא את כל מה שזנד סרבר מציע.
לי חסרה השוואה עם nginx מבחינת ביצועים ויכולות
מצאתי אותו:
http://www.go2linux.org/linux/2011/04/nginx-varnish-compared-nginx-941
ולפי התוצאות נראה כאילו אין הבדל רב מדי בין התוצאות, למאט מקרים שבהם גם הקבצים הסטטיים נשמרים ב ram ואין קריאה מדיסק - ואת זה יודע לעשות רק varnish
מצד שני גם nginx הוא קוד פתוח שקיים הרבה מאוד זמן והיום משרת יותר ממחצית האתרים הגדולים. כמובן ש varnish עדיין בשלבי גדילה והיא כלי נהדר אבל השאלה שלי היא למה בחרתם ב drippler דווקא בה ולא ב nginx
nginx אכן יודע לשמש גם כ proxy וגם לעשות caching אבל לא לשם כך הוא נועד.
משום כך, ל varnish יש הרבה יותר גמישות מבחינת ההגדרות מה להגיש מהcache ומה לא, ובדריפלר אנחנו זקוקים לגמישות הזו כדי לשרת משתמשים אנונימיים וגם משתמשים רשומים וגם להגיע לרמה גבוה יותר של שליטה על expiration של עמודים מהקאש כאשר ידיעות חדשות נכנסות ולא רק לפי זמן מוגדר מראש.
יכול להיות שאשפר לעשות חלק מהדברים האלה גם עם nginx אבל אני לא מכיר מספיק טוב את nginx כדי לומר בוודאות.
הבנתי. תודה :-)
nginx בונה את ה cache שלו או לפי ההדרים שהדפדפן שולח לגבי תוקף העמוד (שהצבנו בעצמינו), לפי קוקי ופרמטרים נכנסים אחרים או לפי מפתח cache שהקוד שלנו מייצר כל פעם אחד שונה כזה כשהפלט משתנה.
יפה מאוד